<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    <title>Chapter12: Groovy configuration</title>
    
    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />

  </head>
  <body onload="prettyPrint(); decorate();">

    <script type="text/javascript">prefix='../'</script>
    <script type="text/javascript" src="../js/prettify.js"></script>
    <script type="text/javascript" src="../js/dsl.js"></script>
    <script type="text/javascript" src="../templates/header.js"></script>
    <script type="text/javascript" src="../js/jquery-min.js"></script>
    <script type="text/javascript" src="../js/decorator.js"></script>

    <div id="left">
      <noscript>Please turn on Javascript to view this menu</noscript>
      <script src="../templates/left.js" type="text/javascript"></script>
    </div>
    <div id="right">
      <script src="menu.js" type="text/javascript"></script>
    </div>
    <div id="content" class="chapter">
      
    <h1>Chapter 12: Groovy Configuration</h1>

    <a href="groovy_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
      

    <div class="quote">
      <p><em>It is better to be a human being dissatisfied than a pig
      satisfied; better to be a Socrates dissatisfied than a fool
      satisfied. And if the fool or the pig thinks otherwise, it is
      because they have no experience of the better part.
      </em>
      </p>
      <p>&mdash;JOHN STUART MILL, <em>Utilitarianism</em></p>
    </div>
    <script src="../templates/creative.js" type="text/javascript"></script>


    <p>Domain-specific languages or DSLs are rather pervasive. The
    XML-based logback configuration can be viewed as a DSL
    instance. By the very nature of XML, XML-based configuration files
    are quite verbose and rather bulky. Moreover, a relatively large
    body of code in logback, namely Joran, is dedicated to processing
    these XML-based configuration files. Joran supports nifty features
    such as variable substitution, conditional processing and
    on-the-fly extensibility. However, not only is Joran a complex
    beast, the user-experience it provides can be described as
    unsatisfactory or at the very least unintuitive.
    </p>

    <p>The Groovy-based DSL described in this chapter aims to be
    consistent, intuitive, and powerful. Everything you can do using XML in
    configuration files, you can do in Groovy with a much shorter
    syntax. To help you migrate to Groovy style configuration, we have
    developed a <a
    href="http://logback.qos.ch/translator/asGroovy.html">tool to
    automatically migrate your existing <em>logback.xml</em> files to
    <em>logback.groovy</em></a>.
    </p>


    <h2 class="doAnchor">General philosophy</h2>
    
    <p>As a general rule, <em>logback.groovy</em> files are Groovy
    programs. And since Groovy is a super-set of Java, whatever
    configuration actions you can perform in Java, you can do the same
    within a <em>logback.groovy</em> file. However, since configuring
    logback programmatically using Java syntax can be cumbersome, we
    added a few logback-specific extensions to make your life
    easier. We try hard to limit the number of logback-specific
    syntactic extensions to an absolute minimum. If you are already
    familiar with Groovy, you should be able to read, understand and
    even write your own <em>logback.groovy</em> files with great
    ease. Those unfamiliar with Groovy should still find
    <em>logback.groovy</em> syntax much more comfortable to use than
    <em>logback.xml</em>.
    </p>

    <p>Given that <em>logback.groovy</em> files are Groovy programs
    with minimal logback-specific extensions, <em>all</em> the usual
    groovy constructs such as class imports, variable definitions,
    evaluation of $&#123;..&#125; expressions contained in strings
    (GStrings), and if-else statements are available in
    <em>logback.groovy</em> files.</p>

    <h2 class="doAnchor">Automatic imports</h2>

    <p><span class="label">Since 1.0.10</span> In order to reduce
    unnecessary boilerplate several common types and packages are
    imported automatically.  Thus, as long as you are only configuring
    built-in appenders, layouts etc. you do not need to add the
    corresponding import statement into your script. You will need
    them for types not covered by the default imports, of course.</p>

    <p>Here is the list of default imports:</p>

    <ul>
      <li><span class="code">import ch.qos.logback.core.*;</span></li>
      <li><span class="code">import ch.qos.logback.core.encoder.*;</span></li>
      <li><span class="code">import ch.qos.logback.core.read.*;</span></li>
      <li><span class="code">import ch.qos.logback.core.rolling.*;</span></li>
      <li><span class="code">import ch.qos.logback.core.status.*;</span></li>
      <li><span class="code">import ch.qos.logback.classic.net.*;</span></li>
      <li><span class="code">import ch.qos.logback.classic.encoder.PatternLayoutEncoder;</span></li>
    </ul>

    <p>In addition, all constants in <span
    class="code">ch.qos.logback.classic.Level</span> are statically
    imported as is (uppercase) and as lowercased aliases. It follows
    that your scripts can reference both <em>INFO</em> or
    <em>info</em> without a static import statement.</p>


    <h2 class="doAnchor" name="sift">SiftingAppender no longer supported</h2>

    <p><span class="label">Since version 1.0.12</span>
    <code>SiftingAppender</code> is no longer supported within groovy
    configuration files. However, in case there is demand, it may be
    re-introduced.</p>

    <h2 class="doAnchor" name="entensions">Extensions specific to
    <em>logback.groovy</em></h2>

    <p><span class="green">Essentially, <em>Logback.groovy</em> syntax
    consists of half a dozen methods described next; in the reverse
    order of their customary appearance. </span>Strictly speaking, the
    order of invocation of these methods does NOT matter, with one
    exception: appenders MUST be defined before they can be attached
    to a logger.</p>

    

    <!-- ========================================================== -->

    <h3> &#8226; <span class="code">root(Level level, List&lt;String> appenderNames = [])</span></h3>

    <p>The <code>root</code> method can be used to set the level of
    the root logger. As an optional second argument of type
    <code>List&lt;String></code>, can be used to attach previously
    defined appenders by name. If you do not specify the list of
    appender names, then an empty list is assumed. In Groovy, an empty
    list is denoted by <code>[]</code>.</p>

    <p>To set the level of the root logger to WARN, you would write:</p>

    <pre class="prettyprint source">root(WARN)</pre>

    <p>To set the level of the root logger to INFO, and attach 
    appenders named "CONSOLE" and "FILE" to root, you would write:</p>

    <pre class="prettyprint source">root(INFO, ["CONSOLE", "FILE"])</pre>

    <p>In the previous example, it is assumed that the appenders named
    "CONSOLE" and "FILE" were already defined. Defining appenders will
    be discussed shortly.
    </p>

    <!-- ========================================================== -->

    <h3>&#8226; <span class="code">logger(String name, Level level, List&lt;String> appenderNames = [], <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean additivity = null)</span></h3>

    <p>The <code>logger()</code> method takes four arguments, of which
    the last two are optional. The first argument is the name of the
    logger to configure. The second argument is the level of the
    designated logger. Setting the level of a logger to
    <code>null</code> forces it to <a
    href="architecture.html#effectiveLevel">inherit its level</a> from
    its nearest ancestor with an assigned level. The third argument of
    type <code>List&lt;String></code> is optional and defaults to an
    empty list if omitted. The appender names in the list are attached
    to the designated logger. The fourth argument of type
    <code>Boolean</code> is also optional and controls the <a
    href="architecture.html#additivity">additivity flag</a>. If
    omitted, it defaults to <code>null</code>.
    </p> 

    <p>For example, the following script sets the level of the
    "com.foo" logger to INFO.</p>

       <pre class="prettyprint source">logger("com.foo", INFO)</pre>

    <p>The next script sets the level of the "com.foo" logger to
    DEBUG, and attaches the appender named "CONSOLE" to it.</p>

  <pre class="prettyprint source">logger("com.foo", DEBUG, ["CONSOLE"])</pre>
    
   <p>The next script is similar to the previous one, except that it
   also sets the the additivity flag of the "com.foo" logger to
   false.</p>

  <pre class="prettyprint source">logger("com.foo", DEBUG, ["CONSOLE"], false)</pre>


    <!-- ========================================================== -->
    <h3>&#8226; <span class="code">appender(String name, Class clazz, Closure closure = null)</span></h3>

    <p>The appender method takes the name of the appender being
    configured as its first argument. The second mandatory argument is
    the class of the appender to instantiate. The third argument is a
    closure containing further configuration instructions. If omitted,
    it defaults to null.</p>

    <p>Most appenders require properties to be set and sub-components
    to be injected to function properly. Properties are set using the
    '=' operator (assignment). Sub-components are injected by invoking
    a method named after the property and passing that method the
    class to instantiate as an argument. This convention can be
    applied recursively to configure properties as well as
    sub-components of any appender sub-component. This approach is at
    the heart of <em>logback.groovy</em> scripts and is probably the
    only convention that needs learning.</p>
    
    <p>For example, the following script instantiates a
    <code>FileAppender</code> named "FILE", setting its <span
    class="option">file</span> property to "testFile.log" and its
    <span class="option">append</span> property to false. An encoder
    of type <code>PatternLayoutEncoder</code> is injected into the
    appender. The pattern property of the encoder is set to "%level
    %logger - %msg%n". The appender is then attached to the root
    logger.</p>

    <pre class="prettyprint source">appender("FILE", FileAppender) {
  file = "testFile.log"
  append = true
  encoder(PatternLayoutEncoder) {
    pattern = "%level %logger - %msg%n"
  }
}

root(DEBUG, ["FILE"])</pre>

    <p>
    </p>

    
    <!-- ========================================================== -->        
    <h3>&#8226; <span class="code">timestamp(String datePattern, long timeReference = -1)</span></h3>

    <p>The <code>timestamp()</code> method method returns a string
    corresponding to the <code>timeReference</code> parameter
    formatted according to the <code>datePattern</code> parameter. The
    <code>datePattern</code> parameter should follow the conventions
    defined by <a
    href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a>. If
    the <code>timeReference</code> value is unspecified, it defaults
    to -1, in which case current time, that is time when the
    configuration file is parsed, is used as the time
    reference. Depending on the circumstances, occasion, you might
    wish to use <code>context.birthTime</code> as the time reference.
    </p>

    <p>In the next example, the <code>bySecond</code> variable is
    assigned the current time in the "yyyyMMdd'T'HHmmss" format. The
    "bySecond" variable is then used to define the value of the <span
    class="option">file</span> property.
    </p>

<pre class="prettyprint source"><b>def bySecond = timestamp("yyyyMMdd'T'HHmmss")</b>

appender("FILE", FileAppender) {
  <b>file = "log-${bySecond}.txt"</b>
  encoder(PatternLayoutEncoder) {
    pattern = "%logger{35} - %msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>

    <!-- ========================================================== -->        
    <h3>&#8226; <span class="code">conversionRule(String conversionWord, Class converterClass)</span></h3>

    <p>After creating your own <a
    href="layouts.html#customConversionSpecifier">conversion
    specifier</a>, you need to inform logback of its existence. Here
    is a sample logback.groovy file which instructs logback to use
    MySampleConverter whenever the <code>%sample</code> conversion
    word is encountered.
    </p>

    <pre class="prettyprint source">
import chapters.layouts.MySampleConverter

conversionRule("sample", MySampleConverter)
appender("STDOUT", ConsoleAppender) {
  encoder(PatternLayoutEncoder) {
    pattern = "%-4relative [%thread] %<b>sample</b> - %msg%n"
  }
}
root(DEBUG, ["STDOUT"])</pre>

   <!-- ========================================================== -->
   <h3>&#8226; <span class="code">scan(String scanPeriod = null)</span></h3>

    <p>Invoking the scan() method instructs logback to periodically
    scan the logback.groovy file for changes. Whenever a change is
    detected, the <em>logback.groovy</em> file is reloaded.</p>

    <pre class="prettyprint source">scan()</pre>

    <p>By default, the configuration file will be scanned for changes
    once every minute. You can specify a different scanning period by
    passing a "scanPeriod" string value. Values can be specified in
    units of milliseconds, seconds, minutes or hours. Here is an
    example:
    </p>

    <pre class="prettyprint source">scan("30 seconds")</pre>
    
    <p>If no unit of time is specified, then the unit of time is
    assumed to be milliseconds, which is usually inappropriate. If you
    change the default scanning period, do not forget to specify a
    time unit. For additional details on how scanning works, please
    refer to the <a href="configuration.html#autoScan">section on
    automatic reloading</a>.
    </p>
    
    <!-- ========================================================== -->
  
    <h3>&#8226; <span class="code">statusListener(Class listenerClass)</span></h3>

    <p>You can add a status listener by invoking the
    <code>statusListener</code> method and passing a listener class as
    an argument. Here is an example:</p>

    <pre class="prettyprint source">import chapters.layouts.MySampleConverter

<b>// We highly recommended that you always add a status listener just</b>
<b>// after the last import statement and before all other statements</b>
<b>statusListener(OnConsoleStatusListener)</b></pre>
 
    <p><a href="configuration.html#statusListener">Status listeners</a> were described in an earlier
    chapter.</p>

    <h3>&#8226; <span class="code">jmxConfigurator(String name)</span></h3>

    <p>You can register a <a href="jmxConfig.html"><code>JMXConfigurator</code></a>
    MBean with this method. Invoke it without any parameters to use Logback's
    default ObjectName
    (<code>ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator</code>)
    for the registered MBean:</p>

    <pre class="prettyprint source">jmxConfigurator()</pre>

    <p>To change the value of the <code>Name</code> key to something other than "default",
    simply pass in a different name as the parameter for the <code>jmxConfigurator</code>
    method:</p>

    <pre class="prettyprint source">jmxConfigurator('MyName')</pre>

    <p>If you want define the ObjectName completely, use the same syntax but
    pass in a valid ObjectName string representation as the parameter:</p>

    <pre class="prettyprint source">jmxConfigurator('myApp:type=LoggerManager')</pre>

    <p>The method will first attempt to use the parameter as an ObjectName,
    and falls back to treating it as the value for the "Name" key if it doesn't
    represent a valid ObjectName.</p>

    <!-- ========================================================== -->

    <h2 class="doAnchor" name="internalDSL">Internal DSL, i.e. it's
    all groovy baby!</h2>

    <p>The <em>logback.groovy</em> is an internal DSL meaning that its
    contents are executed as a Groovy script. Thus, all the usual
    Groovy constructs such as class imports, GString, variable
    definitions, evaluation of $&#123;..&#125; expressions contained
    within strings (GStrings), if-else statements are all available in
    logback.groovy files. In the following discussion, we will present
    typical uses of these Groovy constructs in <em>logback.groovy</em>
    files.
    </p>


    <h3 class="doAnchor" name="varedef">Variable definitions and
    GStrings</h3>

    <p>You can define variables anywhere within a
    <em>logback.groovy</em> file, then use the variable within a
    GString. Here is an example.</p>

    <pre class="prettyprint source">// define the USER_HOME variable setting its value 
// to that of the "user.home" system property
<b>def USER_HOME = System.getProperty("user.home")</b>

appender("FILE", FileAppender) {
  // make use of the USER_HOME variable
  <b>file = "${USER_HOME}/myApp.log"</b>
  encoder(PatternLayoutEncoder) {
    pattern = "%msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>


    <h3 class="doAnchor" name="printing">Printing on the console</h3>

    <p>You can invoke Groovy's <code>println</code> method to print on
    the console. Here is an example.</p>

    <pre class="prettyprint source">def USER_HOME = System.getProperty("user.home");
<b>println "USER_HOME=${USER_HOME}"</b>

appender("FILE", FileAppender) {
  <b>println "Setting [file] property to [${USER_HOME}/myApp.log]"</b>
  file = "${USER_HOME}/myApp.log"  
  encoder(PatternLayoutEncoder) {
    pattern = "%msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>


   <h3 class="doAnchor" name="automaticallyExported">Automatically
   exported fields</h3>

   <h4 class="doAnchor" name="hostname">'hostname' variable</h4>

   <p>The 'hostname' variable contains the name of the current
   host. However, due to scoping rules that the authors cannot fully
   explain, the 'hostname' variable is available only at the topmost
   scope but not in nested scopes. The next example should get the
   point across.
   </p>

 <pre class="prettyprint source">// will print "hostname is x" where x is the current host's name
println "Hostname is ${hostname}"

appender("STDOUT", ConsoleAppender) {
  <b>// will print "hostname is null"</b>
  <b>println "Hostname is ${hostname}" </b>
}</pre>

   <p>If you wish to have the hostname variable be seen in all scopes,
   you need to define another variable and assign it the value of
   'hostname' as shown next.</p>

 <pre class="prettyprint source">// define HOSTNAME by assigning it hostname
def HOSTNAME=hostname
// will print "hostname is x" where x is the current host's name
println "Hostname is ${HOSTNAME}"

appender("STDOUT", ConsoleAppender) {
  // will print "hostname is x" where x is the current host's name
  println "Hostname is ${HOSTNAME}" 
}</pre>


   <h3 class="doAnchor" name="everythingIsContext">Everything is
   context aware with a reference to the current context</h3>

   <p>The execution of the <em>logback.groovy</em> script is done
   within the scope of a <a
   href="../xref/ch/qos/logback/core/spi/ContextAware.html">ContextAware</a>
   object. Thus, the current context is always accessible using the
   '<code>context</code>' variable and you can invoke
   <code>addInfo</code>(), <code>addWarn</code>() and
   <code>addError</code>() methods to send status messages to the
   context's <code>StatusManager</code>.</p>

   <pre class="prettyprint source">// always a good idea to add an on console status listener
statusListener(OnConsoleStatusListener)

// set the context's name to wombat
<b>context.name = "wombat"</b>
// add a status message regarding context's name
<b>addInfo("Context name has been set to ${context.name}")</b>

def USER_HOME = System.getProperty("user.home");
// add a status message regarding USER_HOME
<b>addInfo("USER_HOME=${USER_HOME}")</b>

appender("FILE", FileAppender) {
  // add a status message regarding the file property
  <b>addInfo("Setting [file] property to [${USER_HOME}/myApp.log]")</b>
  file = "${USER_HOME}/myApp.log"  
  encoder(PatternLayoutEncoder) {
    pattern = "%msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>


   <h3 class="doAnchor">Conditional configuration</h3>
   
   <p>Given that Groovy is a fully-fledged programming language,
   conditional statements allow for a single <em>logback.groovy</em>
   file to adapt to various environments such as development, testing
   or production.</p>

   <p>In the next script, a console appender is activated on hosts
   other than pixie or orion, our production machines. Note that the
   output directory of the rolling file appender also depends on the
   host.</p>
   
   <pre class="prettyprint source">// always a good idea to add an on console status listener
statusListener(OnConsoleStatusListener)

def appenderList = ["ROLLING"]
def WEBAPP_DIR = "."
def consoleAppender = true;

// does hostname match pixie or orion?
if (hostname =~ /pixie|orion/) {
  WEBAPP_DIR = "/opt/myapp"     
  consoleAppender = false   
} else {
  appenderList.add("CONSOLE")
}

if (consoleAppender) {
  appender("CONSOLE", ConsoleAppender) {
    encoder(PatternLayoutEncoder) {
      pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    }
  }
}

appender("ROLLING", RollingFileAppender) {
  encoder(PatternLayoutEncoder) {
    Pattern = "%d %level %thread %mdc %logger - %m%n"
  }
  rollingPolicy(TimeBasedRollingPolicy) {
    FileNamePattern = "${WEBAPP_DIR}/log/translator-%d{yyyy-MM}.zip"
  }
}

root(INFO, appenderList)</pre>




    <script src="../templates/footer.js" type="text/javascript"></script>

    </div>
  </body>
</html>
